package com.tj.bo.trade.server.service;

import com.fqgj.common.api.annotations.ParamsValidate;
import com.fqgj.exception.common.ApplicationException;
import com.tj.bo.account.client.AccountBalanceChangeService;
import com.tj.bo.account.client.enums.ExpenditureEnum;
import com.tj.bo.account.client.vo.AccountExpenditureVO;
import com.tj.bo.common.conf.util.DateUtils;
import com.tj.bo.common.conf.util.SerialNumberUtil;
import com.tj.bo.trade.client.TradeService;
import com.tj.bo.trade.client.enums.OrderStatusEnum;
import com.tj.bo.trade.client.enums.TradeTypeEnum;
import com.tj.bo.trade.client.request.BinaryTradeExtra;
import com.tj.bo.trade.client.request.CreateTradeRequestRO;
import com.tj.bo.trade.client.response.CreateTradeResponseRO;
import com.tj.bo.trade.server.dao.BinaryTradeDao;
import com.tj.bo.trade.server.dao.BinaryTradeExtraDao;
import com.tj.bo.trade.server.dao.BinaryTradeFlowDao;
import com.tj.bo.trade.server.entity.BinaryTradeEntity;
import com.tj.bo.trade.server.entity.BinaryTradeExtraEntity;
import com.tj.bo.trade.server.entity.BinaryTradeFlowEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;

/**
 * @program: binary-option
 * @description
 * @author: binary
 * @create: 2020-06-05 14:37
 **/
@Service
@Transactional(rollbackFor = Exception.class)
public class TradeServiceImpl implements TradeService {

    private static final Logger LOGGER = LoggerFactory.getLogger(TradeServiceImpl.class);

    @Autowired
    private BinaryTradeDao binaryTradeDao;
    @Autowired
    private BinaryTradeExtraDao binaryTradeExtraDao;
    @Autowired
    private BinaryTradeFlowDao binaryTradeFlowDao;
    @Autowired
    private AccountBalanceChangeService accountBalanceChangeService;

    @Override
    @ParamsValidate
    public CreateTradeResponseRO createTrade(CreateTradeRequestRO request, BinaryTradeExtra binaryTradeExtra) {

        String tradeNo = this.createTradeNo(request.getUserCode());
        long startTime = System.currentTimeMillis();
        BinaryTradeEntity entity = new BinaryTradeEntity();
        BeanUtils.copyProperties(request, entity);
        entity.setCreateDate(new Date());
        entity.setOrderStatus(OrderStatusEnum.OPENING.getCode());
        entity.setTradeNo(tradeNo);
        entity.setExpectDeadline(DateUtils.addDate(new Date(), request.getProductPeriod()));
        entity.setTheoryExpectDeadline(DateUtils.addDate(entity.getOpeningTime(), request.getProductPeriod()));
        binaryTradeDao.insert(entity);
        LOGGER.info("submit order insert order cost :{}", System.currentTimeMillis() - startTime);

        BinaryTradeExtraEntity binaryTradeExtraEntity = new BinaryTradeExtraEntity();
        binaryTradeExtraEntity.setTradeNo(tradeNo);
        binaryTradeExtraEntity.setUserCode(request.getUserCode());
        binaryTradeExtraEntity.setIp(binaryTradeExtra.getIp());
        binaryTradeExtraEntity.setVersion(binaryTradeExtra.getVersion());
        binaryTradeExtraEntity.setVersionCode(binaryTradeExtra.getVersionCode());
        binaryTradeExtraEntity.setAppCode(binaryTradeExtra.getAppCode());
        binaryTradeExtraEntity.setPackageName(binaryTradeExtra.getPackageName());
        binaryTradeExtraDao.insert(binaryTradeExtraEntity);

        //扣减余额
        this.deductionBalance(request.getUserCode(), tradeNo, request.getAmount());
        //记录流水
        this.insertTradeFlow(tradeNo);
        Integer ordersNumbers = 0;
        if (Objects.equals(request.getTradeType(), TradeTypeEnum.VIRTUAL_TRADE.getCode())) {
            ordersNumbers = binaryTradeDao.countBinaryTrade(request.getUserCode(), null, request.getTradeType());
        }

        LOGGER.info("============== submit order insert success, userCode:{},productCode:{} =============", request.getUserCode(), request.getProductCode());
        return new CreateTradeResponseRO(tradeNo, request.getAmount().toString(), request.getTrendType(), entity.getOpeningTime().getTime(), request.getOpeningPrice().toString(), ordersNumbers);
    }

    private String createTradeNo(String userCode) {
        try {
            long startTime = System.currentTimeMillis();
            String tradeNo = SerialNumberUtil.getBizOrderNumber("B", "O");
            LOGGER.info("submit order create orderNo cost :{}", System.currentTimeMillis() - startTime);
            return tradeNo;
        } catch (Exception e) {
            LOGGER.error("================== 生成tradeNo异常,userCode:{},e =================", userCode, e);
            throw new ApplicationException("create trade no error");
        }
    }

    private void insertTradeFlow(String tradeNo) {
        BinaryTradeFlowEntity binaryTradeFlowEntity = new BinaryTradeFlowEntity()
                .setOrderStatus(OrderStatusEnum.OPENING.getCode())
                .setCreateDate(new Date())
                .setTradeNo(tradeNo);
        binaryTradeFlowDao.insert(binaryTradeFlowEntity);
    }

    private void deductionBalance(String userCode, String tradeNo, BigDecimal amount) {
        AccountExpenditureVO accountExpenditure = new AccountExpenditureVO();
        accountExpenditure.setUserCode(userCode);
        accountExpenditure.setOrderNo(tradeNo);
        accountExpenditure.setAmount(amount);
        accountExpenditure.setExpenditureEnum(ExpenditureEnum.TRADE_BUY);
        if (!accountBalanceChangeService.expenditure(accountExpenditure)) {
            LOGGER.warn("扣减余额业务异常,userCode:{},tradeNo:{},amount:{}", userCode, tradeNo, amount);
            throw new ApplicationException("Your account has insufficient funds, please deposit");
        }
    }
}
